﻿/*
Author : Onur "Xtro" ER
Create date : 08.08.2009
Last update : 28.12.2011
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using Xtro.MDX;
using Xtro.MDX.DXGI;
using Xtro.MDX.Direct3D10;
using Xtro.MDX.Direct3DX10;

using Error = Xtro.MDX.Direct3D10.Error;
using Resource = Xtro.MDX.Direct3D10.Resource;
using Functions = Xtro.MDX.Direct3DX10.Functions;

using Xtro.Gerecler;

namespace Xtro.DirectX.Direct3D
{
    public sealed class TTexture
    {
        public enum NType { Line, Normal, Cube, Volume }
        public enum NCompression { None, BC2, BC3 }

        public string Name;
        public TContext Context { get; private set; }

        NType FType = NType.Normal;
        bool FIsDepthStencil;

        public bool IsDepthStencil
        {
            get { return FIsDepthStencil; }
            set
            {
                if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

                if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);

                if (value)
                {
                    if (CpuAccess != 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureWithCpuAccessCantBeDepthStencil, Name);
                    if (FCompression != NCompression.None) throw new EInvalidCall(Name, GetType(), ResourcesReason.DepthStencilTextureCantBeCompressed, Name);
                    if (FType == NType.Cube || FType == NType.Volume) throw new EInvalidCall(Name, GetType(), ResourcesReason.VolumeOrCubeTextureCantBeDepthStencil, Name);
                }

                FIsDepthStencil = value;
            }
        }

        uint FArrayLength = 1;

        public uint ArrayLength
        {
            get { return FArrayLength; }
            set
            {
                if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

                if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);

                var Minimum = 1;
                int Maximum;
                switch (FType)
                {
                case NType.Cube:
                    Minimum = 6;
                    Maximum = 6;
                    break;
                case NType.Volume:
                    Maximum = 1;
                    break;
                default:
                    Maximum = 512;
                    break;
                }

                if (value < Minimum || value > Maximum) throw new EInvalidParameter("value", value.GetType(), string.Format(Gerecler.ResourcesReason.OutOfBounds, value.ToString()), Name);

                FArrayLength = value;
            }
        }

        bool MipLevelAutomated;
        uint FMipLevels = 1;

        /// <summary>
        /// Use 0 to generate a full set of subtextures. Default is 1.
        /// </summary>
        public uint MipLevels
        {
            get { return FMipLevels; }
            set
            {
                if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

                if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);

                if (value > 0)
                {
                    if (GetMaximumSizeElement(FType, ref FSize) < Math.Pow(2, value - 1)) throw new EInvalidParameter("value", value.GetType(), string.Format(ResourcesReason.SizeAndMipLevelsNotMatching, value.ToString()), Name);
                }

                FMipLevels = value;
            }
        }

        static int GetMaximumSizeElement(NType Type, ref SSize3D Size)
        {
            var Result = Size.Width;

            if (Type != NType.Line) Result = Math.Max(Result, Size.Height);
            if (Type == NType.Volume) Result = Math.Max(Result, Size.Depth);

            return Result;
        }

        NCompression FCompression = NCompression.BC3;

        public NCompression Compression
        {
            get { return FCompression; }
            set
            {
                if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

                if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);

                if (value != NCompression.None)
                {
                    if (FIsDepthStencil) throw new EInvalidCall(Name, GetType(), ResourcesReason.DepthStencilTextureCantBeCompressed, Name);
                    if (FType == NType.Line) throw new EInvalidCall(Name, GetType(), ResourcesReason.LineTextureCantBeCompressed, Name);
                    if (FType == NType.Cube && FSize.Width % 4 > 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureSizeMustBeMultipleOf4ToBeCompressed, Name);
                    if (FType == NType.Normal && (FSize.Width % 4 > 0 || FSize.Height % 4 > 0)) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureSizeMustBeMultipleOf4ToBeCompressed, Name);
                    if (FType == NType.Volume && (FSize.Width % 4 > 0 || FSize.Height % 4 > 0 || FSize.Depth % 4 > 0)) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureSizeMustBeMultipleOf4ToBeCompressed, Name);
                }

                FCompression = value;
            }
        }

        SSize3D FSize = new SSize3D(100, 100, 100);

        internal Resource Resource { get; private set; }

        public NType Type
        {
            get { return FType; }
            set
            {
                if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

                if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);

                if (FIsDepthStencil && (value == NType.Volume || value == NType.Cube)) throw new EInvalidCall(Name, GetType(), ResourcesReason.VolumeOrCubeTextureCantBeDepthStencil, Name);

                if (value == NType.Volume)
                {
                    if (FSize.Width > 2048 || FSize.Height > 2048 || FSize.Depth > 2048) throw new EInvalidCall(Name, GetType(), ResourcesReason.SizeTooBigForVolumeTexture, Name);
                }

                if (FMipLevels > 0)
                {
                    if (GetMaximumSizeElement(value, ref FSize) < Math.Pow(2, FMipLevels - 1)) throw new EInvalidParameter("value", value.GetType(), string.Format(ResourcesReason.SizeAndMipLevelsNotMatching, value.ToString()), Name);
                }

                if (value == NType.Line && FCompression != NCompression.None) throw new EInvalidCall(Name, GetType(), ResourcesReason.LineTextureCantBeCompressed, Name);

                switch (value)
                {
                case NType.Cube:
                    FArrayLength = 6;
                    break;
                case NType.Volume:
                    FArrayLength = 1;
                    break;
                }

                FType = value;
            }
        }

        /// <summary>
        /// Use Size.Empty for auto-sizing. Default is Size.Empty
        /// </summary>
        public SSize3D Size
        {
            get
            {
                if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

                var Result = FSize;

                switch (FType)
                {
                case NType.Line:
                    Result.Height = 1;
                    Result.Depth = 1;
                    break;
                case NType.Normal:
                    Result.Depth = 1;
                    break;
                case NType.Cube:
                    Result.Height = Result.Width;
                    Result.Depth = 1;
                    break;
                }

                return Result;
            }
            set
            {
                if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

                if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);

                if (value.Width < 1 || value.Width > 8192 || value.Height < 1 || value.Height > 8192 || value.Depth < 1 || value.Depth > 8192) throw new EInvalidParameter("value", value.GetType(), string.Format(Gerecler.ResourcesReason.OutOfBounds, value.ToString()), Name);

                if (FType == NType.Volume && (value.Width > 2048 || value.Height > 2048 || value.Depth > 2048)) throw new EInvalidCall(Name, GetType(), ResourcesReason.SizeTooBigForVolumeTexture, Name);

                if (FMipLevels > 0)
                {
                    if (GetMaximumSizeElement(FType, ref value) < Math.Pow(2, FMipLevels - 1)) throw new EInvalidParameter("value", value.GetType(), string.Format(ResourcesReason.SizeAndMipLevelsNotMatching, value.ToString()), Name);
                }

                if (FCompression != NCompression.None)
                {
                    if (FType == NType.Cube && (value.Width % 4 > 0)) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureSizeMustBeMultipleOf4ToBeCompressed, Name);
                    if (FType == NType.Normal && (value.Width % 4 > 0 || value.Height % 4 > 0)) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureSizeMustBeMultipleOf4ToBeCompressed, Name);
                    if (FType == NType.Volume && (value.Width % 4 > 0 || value.Height % 4 > 0 || value.Depth % 4 > 0)) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureSizeMustBeMultipleOf4ToBeCompressed, Name);
                }

                FSize = value;
            }
        }

        public readonly CpuAccessFlag CpuAccess;

        public TTexture(string Name, TContext Context, CpuAccessFlag CpuAccess = (CpuAccessFlag)0)
        {
            if (Context == null) throw new EInvalidParameter("Context", typeof(TContext), Gerecler.ResourcesReason.NullReference, Name);

            this.Name = Name;
            this.Context = Context;
            this.CpuAccess = CpuAccess;

            Context.Textures.Add(this);
        }

        bool CanStop = true;

        public bool Active { get { return Resource != null; } }

        public void Stop()
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);
            if (!CanStop) throw new EInvalidCall(Name, GetType(), ResourcesReason.CantStopInternalObject, Name);

            UnmapAll();

            DeleteTexture();

            if (MipLevelAutomated) FMipLevels = 0;
        }

        internal void StopInternal()
        {
            CanStop = true;
            Stop();
        }

        public void Delete()
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource != null) Stop();

            Context.Textures.Remove(this);
            Context = null;
        }

        RenderTargetView RenderTargetView;
        DepthStencilView DepthStencilView;

        void CreateViews()
        {
            if (CpuAccess != 0) return;

            if (FIsDepthStencil)
            {
                var Result = Context.Device.CreateDepthStencilView(Resource, out DepthStencilView);
                if (Result != 0) throw new EDirectX("Device.CreateDepthStencilView", Result, Name);
            }
            else
            {
                if (FCompression == NCompression.None)
                {
                    var Result = Context.Device.CreateRenderTargetView(Resource, out RenderTargetView);
                    if (Result != 0) throw new EDirectX("Device.CreateRenderTargetView", Result, Name);
                }
            }
        }

        void DeleteViews()
        {
            if (RenderTargetView != null)
            {
                RenderTargetView.Release();
                RenderTargetView = null;
            }

            if (DepthStencilView != null)
            {
                DepthStencilView.Release();
                DepthStencilView = null;
            }
        }

        void DeleteTexture()
        {
            DeleteViews();

            if (Resource == null) return;

            Resource.Release();
            Resource = null;
        }

        public sealed class TImageFileInfo
        {
            public string FileName;
            public FilterFlag FilterFlags = FilterFlag.None;
            public FilterFlag MipFilterFlags = FilterFlag.None;
            public uint FirstMipLevel;
        }

        public sealed class TImageMemoryFileInfo
        {
            public UnmanagedMemory MemoryFile;
            public FilterFlag FilterFlags = FilterFlag.None;
            public FilterFlag MipFilterFlags = FilterFlag.None;
            public uint FirstMipLevel;
        }

        void CreateTexture(object Data)
        {
            DeleteTexture();

            switch (FType)
            {
            case NType.Line:
                #region Texture1D

                var Texture1DDescription = new Texture1D_Description
                {
                    Usage = CpuAccess != 0 ? Usage.Staging : Usage.Default,
                    CpuAccessFlags = CpuAccess,
                    ArraySize = FArrayLength,
                    Width = (uint)FSize.Width,
                    MipLevels = FMipLevels,
                    MiscellaneousFlags = 0
                };

                if (FIsDepthStencil)
                {
                    Texture1DDescription.BindFlags = BindFlag.DepthStencil;
                    Texture1DDescription.Format = Format.D24_UNorm_S8_UInt;
                }
                else
                {
                    Texture1DDescription.BindFlags = CpuAccess != 0 ? 0 : BindFlag.ShaderResource;
                    Texture1DDescription.Format = Format.R8G8B8A8_UNorm;
                    if (CpuAccess == 0)
                    {
                        Texture1DDescription.BindFlags |= BindFlag.RenderTarget;
                        Texture1DDescription.MiscellaneousFlags |= ResourceMiscellaneousFlag.GenerateMips;
                    }
                }

                if (Data == null || Data is SubResourceData[])
                {
                    Texture1D Out;
                    var Result = Context.Device.CreateTexture1D(ref Texture1DDescription, (SubResourceData[])Data, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result != 0) throw new EDirectX("Device.CreateTexture1D", Result, Name);
                    Resource = Out;
                }
                else if (Data is TImageFileInfo)
                {
                    var ImageFileInfo = (TImageFileInfo)Data;

                    var ImageLoadInfo = new ImageLoadInfo
                    {
                        BindFlags = Texture1DDescription.BindFlags,
                        CpuAccessFlags = Texture1DDescription.CpuAccessFlags,
                        Format = Texture1DDescription.Format,
                        MipLevels = Texture1DDescription.MipLevels,
                        MiscellaneousFlags = Texture1DDescription.MiscellaneousFlags,
                        Usage = Texture1DDescription.Usage,
                        Width = Texture1DDescription.Width,
                        Height = 1,
                        Depth = 1,
                        Filter = ImageFileInfo.FilterFlags,
                        MipFilter = ImageFileInfo.MipFilterFlags,
                        FirstMipLevel = ImageFileInfo.FirstMipLevel
                    };

                    Resource Out;
                    var Result = Functions.CreateTextureFromFile(Context.Device, ImageFileInfo.FileName, ref ImageLoadInfo, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result == (int)Error.FileNotFound) throw new ENotAvailable(string.Format(ResourcesCommon.FileToLoad, ImageFileInfo.FileName), Name);
                    if (Result != 0) throw new EDirectX("CreateTextureFromFile", Result, Name);
                    Resource = Out;
                }
                else if (Data is TImageMemoryFileInfo)
                {
                    var ImageFileInfo = (TImageMemoryFileInfo)Data;

                    var ImageLoadInfo = new ImageLoadInfo
                    {
                        BindFlags = Texture1DDescription.BindFlags,
                        CpuAccessFlags = Texture1DDescription.CpuAccessFlags,
                        Format = Texture1DDescription.Format,
                        MipLevels = Texture1DDescription.MipLevels,
                        MiscellaneousFlags = Texture1DDescription.MiscellaneousFlags,
                        Usage = Texture1DDescription.Usage,
                        Width = Texture1DDescription.Width,
                        Height = 1,
                        Depth = 1,
                        Filter = ImageFileInfo.FilterFlags,
                        MipFilter = ImageFileInfo.MipFilterFlags,
                        FirstMipLevel = ImageFileInfo.FirstMipLevel
                    };

                    Resource Out;
                    var Result = Functions.CreateTextureFromMemory(Context.Device, ImageFileInfo.MemoryFile, ImageFileInfo.MemoryFile.Size, ref ImageLoadInfo, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result != 0) throw new EDirectX("CreateTextureFromMemory", Result, Name);
                    Resource = Out;
                }
                else throw new EGerecler(Name, GetType(), Gerecler.ResourcesReason.UnexpectedInternalError, Name);

                if (!(Resource is Texture1D))
                {
                    DeleteTexture();

                    throw new EInvalidCall(Name, GetType(), ResourcesReason.TypeOfLoadedTextureNotMatching, Name);
                }

                Texture1D_Description Resource1DDescription;
                ((Texture1D)Resource).GetDescription(out Resource1DDescription);

                MipLevelAutomated = FMipLevels == 0;
                FMipLevels = Resource1DDescription.MipLevels;

                break;

                #endregion
            case NType.Normal:
            case NType.Cube:
                #region Texture2D

                var Texture2DDescription = new Texture2D_Description
                {
                    Usage = CpuAccess != 0 ? Usage.Staging : Usage.Default,
                    CpuAccessFlags = CpuAccess,
                    SampleDescription = new SampleDescription { Count = 1, Quality = 0 },
                    ArraySize = FArrayLength,
                    Width = (uint)FSize.Width,
                    MipLevels = FMipLevels,
                    MiscellaneousFlags = 0
                };

                if (FType == NType.Cube)
                {
                    Texture2DDescription.Height = (uint)FSize.Width;
                    Texture2DDescription.MiscellaneousFlags |= ResourceMiscellaneousFlag.TextureCube;
                }
                else Texture2DDescription.Height = (uint)FSize.Height;

                if (FIsDepthStencil)
                {
                    Texture2DDescription.BindFlags = BindFlag.DepthStencil;
                    Texture2DDescription.Format = Format.D24_UNorm_S8_UInt;
                }
                else
                {
                    Texture2DDescription.BindFlags = CpuAccess != 0 ? 0 : BindFlag.ShaderResource;

                    switch (FCompression)
                    {
                    case NCompression.BC2:
                        Texture2DDescription.Format = Format.BC2_UNorm;
                        break;
                    case NCompression.BC3:
                        Texture2DDescription.Format = Format.BC3_UNorm;
                        break;
                    default:
                        Texture2DDescription.Format = Format.R8G8B8A8_UNorm;
                        if (CpuAccess == 0)
                        {
                            Texture2DDescription.BindFlags |= BindFlag.RenderTarget;
                            Texture2DDescription.MiscellaneousFlags |= ResourceMiscellaneousFlag.GenerateMips;
                        }
                        break;
                    }
                }

                if (Data == null || Data is SubResourceData[])
                {
                    Texture2D Out;
                    var Result = Context.Device.CreateTexture2D(ref Texture2DDescription, (SubResourceData[])Data, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result != 0) throw new EDirectX("Device.CreateTexture2D", Result, Name);
                    Resource = Out;
                }
                else if (Data is TImageFileInfo)
                {
                    var ImageFileInfo = (TImageFileInfo)Data;

                    var ImageLoadInfo = new ImageLoadInfo
                    {
                        BindFlags = Texture2DDescription.BindFlags,
                        CpuAccessFlags = Texture2DDescription.CpuAccessFlags,
                        Format = Texture2DDescription.Format,
                        MipLevels = Texture2DDescription.MipLevels,
                        MiscellaneousFlags = Texture2DDescription.MiscellaneousFlags,
                        Usage = Texture2DDescription.Usage,
                        Width = Texture2DDescription.Width,
                        Height = Texture2DDescription.Height,
                        Depth = 1,
                        Filter = ImageFileInfo.FilterFlags,
                        MipFilter = ImageFileInfo.MipFilterFlags,
                        FirstMipLevel = ImageFileInfo.FirstMipLevel
                    };

                    Resource Out;
                    var Result = Functions.CreateTextureFromFile(Context.Device, ImageFileInfo.FileName, ref ImageLoadInfo, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result == (int)Error.FileNotFound) throw new ENotAvailable(string.Format(ResourcesCommon.FileToLoad, ImageFileInfo.FileName), Name);
                    if (Result != 0) throw new EDirectX("CreateTextureFromFile", Result, Name);
                    Resource = Out;
                }
                else if (Data is TImageMemoryFileInfo)
                {
                    var ImageFileInfo = (TImageMemoryFileInfo)Data;

                    var ImageLoadInfo = new ImageLoadInfo
                    {
                        BindFlags = Texture2DDescription.BindFlags,
                        CpuAccessFlags = Texture2DDescription.CpuAccessFlags,
                        Format = Texture2DDescription.Format,
                        MipLevels = Texture2DDescription.MipLevels,
                        MiscellaneousFlags = Texture2DDescription.MiscellaneousFlags,
                        Usage = Texture2DDescription.Usage,
                        Width = Texture2DDescription.Width,
                        Height = Texture2DDescription.Height,
                        Depth = 1,
                        Filter = ImageFileInfo.FilterFlags,
                        MipFilter = ImageFileInfo.MipFilterFlags,
                        FirstMipLevel = ImageFileInfo.FirstMipLevel
                    };

                    Resource Out;
                    var Result = Functions.CreateTextureFromMemory(Context.Device, ImageFileInfo.MemoryFile, ImageFileInfo.MemoryFile.Size, ref ImageLoadInfo, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result != 0) throw new EDirectX("CreateTextureFromMemory", Result, Name);
                    Resource = Out;
                }
                else throw new EGerecler(Name, GetType(), Gerecler.ResourcesReason.UnexpectedInternalError, Name);

                Texture2D_Description Resource2DDescription;
                if (Resource is Texture2D) ((Texture2D)Resource).GetDescription(out Resource2DDescription);
                else Resource2DDescription = new Texture2D_Description();

                if (!(Resource is Texture2D) || (Resource2DDescription.MiscellaneousFlags & ResourceMiscellaneousFlag.TextureCube) > 0)
                {
                    DeleteTexture();

                    throw new EInvalidCall(Name, GetType(), ResourcesReason.TypeOfLoadedTextureNotMatching, Name);
                }

                MipLevelAutomated = FMipLevels == 0;
                FMipLevels = Resource2DDescription.MipLevels;

                break;

                #endregion
            case NType.Volume:
                #region Texture3D

                var Texture3DDescription = new Texture3D_Description
                {
                    Usage = CpuAccess != 0 ? Usage.Staging : Usage.Default,
                    CpuAccessFlags = CpuAccess,
                    Width = (uint)FSize.Width,
                    Height = (uint)FSize.Height,
                    Depth = (uint)FSize.Depth,
                    MipLevels = FMipLevels,
                    MiscellaneousFlags = 0,
                    BindFlags = CpuAccess != 0 ? 0 : BindFlag.ShaderResource
                };

                switch (FCompression)
                {
                case NCompression.BC2:
                    Texture3DDescription.Format = Format.BC2_UNorm;
                    break;
                case NCompression.BC3:
                    Texture3DDescription.Format = Format.BC3_UNorm;
                    break;
                default:
                    Texture3DDescription.Format = Format.R8G8B8A8_UNorm;
                    if (CpuAccess == 0)
                    {
                        Texture3DDescription.BindFlags |= BindFlag.RenderTarget;
                        Texture3DDescription.MiscellaneousFlags |= ResourceMiscellaneousFlag.GenerateMips;
                    }
                    break;
                }

                if (Data == null || Data is SubResourceData[])
                {
                    Texture3D Out;
                    var Result = Context.Device.CreateTexture3D(ref Texture3DDescription, (SubResourceData[])Data, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result != 0) throw new EDirectX("Device.CreateTexture3D", Result, Name);
                    Resource = Out;
                }
                else if (Data is TImageFileInfo)
                {
                    var ImageFileInfo = (TImageFileInfo)Data;

                    var ImageLoadInfo = new ImageLoadInfo
                    {
                        BindFlags = Texture3DDescription.BindFlags,
                        CpuAccessFlags = Texture3DDescription.CpuAccessFlags,
                        Format = Texture3DDescription.Format,
                        MipLevels = Texture3DDescription.MipLevels,
                        MiscellaneousFlags = Texture3DDescription.MiscellaneousFlags,
                        Usage = Texture3DDescription.Usage,
                        Width = Texture3DDescription.Width,
                        Height = Texture3DDescription.Height,
                        Depth = Texture3DDescription.Depth,
                        Filter = ImageFileInfo.FilterFlags,
                        MipFilter = ImageFileInfo.MipFilterFlags,
                        FirstMipLevel = ImageFileInfo.FirstMipLevel
                    };

                    Resource Out;
                    var Result = Functions.CreateTextureFromFile(Context.Device, ImageFileInfo.FileName, ref ImageLoadInfo, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result == (int)Error.FileNotFound) throw new ENotAvailable(string.Format(ResourcesCommon.FileToLoad, ImageFileInfo.FileName), Name);
                    if (Result != 0) throw new EDirectX("CreateTextureFromFile", Result, Name);
                    Resource = Out;
                }
                else if (Data is TImageMemoryFileInfo)
                {
                    var ImageFileInfo = (TImageMemoryFileInfo)Data;

                    var ImageLoadInfo = new ImageLoadInfo
                    {
                        BindFlags = Texture3DDescription.BindFlags,
                        CpuAccessFlags = Texture3DDescription.CpuAccessFlags,
                        Format = Texture3DDescription.Format,
                        MipLevels = Texture3DDescription.MipLevels,
                        MiscellaneousFlags = Texture3DDescription.MiscellaneousFlags,
                        Usage = Texture3DDescription.Usage,
                        Width = Texture3DDescription.Width,
                        Height = Texture3DDescription.Height,
                        Depth = Texture3DDescription.Depth,
                        Filter = ImageFileInfo.FilterFlags,
                        MipFilter = ImageFileInfo.MipFilterFlags,
                        FirstMipLevel = ImageFileInfo.FirstMipLevel
                    };

                    Resource Out;
                    var Result = Functions.CreateTextureFromMemory(Context.Device, ImageFileInfo.MemoryFile, ImageFileInfo.MemoryFile.Size, ref ImageLoadInfo, out Out);
                    if (Result == (int)Windows.EError.OutOfMemory) throw new ENotAvailable(ResourcesCommon.RequiredResourceMemory, Name);
                    if (Result != 0) throw new EDirectX("CreateTextureFromMemory", Result, Name);
                    Resource = Out;
                }
                else throw new EGerecler(Name, GetType(), Gerecler.ResourcesReason.UnexpectedInternalError, Name);

                if (!(Resource is Texture3D))
                {
                    DeleteTexture();

                    throw new EInvalidCall(Name, GetType(), ResourcesReason.TypeOfLoadedTextureNotMatching, Name);
                }

                Texture3D_Description Resource3DDescription;
                ((Texture3D)Resource).GetDescription(out Resource3DDescription);

                MipLevelAutomated = FMipLevels == 0;
                FMipLevels = Resource3DDescription.MipLevels;

                break;

                #endregion
            }

            CreateViews();
        }

        public void GenerateMipMaps(uint SourceLevel, FilterFlag MipFilter)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (CpuAccess != 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureWithCpuAccessCantGenerateMipMaps, Name);
            if (MappedSubResources.Count > 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.CantExecuteWhileAnySubResourceIsMapped, Name);

            Functions.FilterTexture(Resource, SourceLevel, MipFilter);
        }

        readonly SortedList<uint, uint> MappedSubResources = new SortedList<uint, uint>();

        public MappedTexture3D Map(uint ElementNo, uint MipNo, Map Type)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (CpuAccess == 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.TextureWithoutCpuAccessCantBeMapped, Name);
            if (ElementNo >= FArrayLength) throw new EInvalidParameter("ElementNo", ElementNo.GetType(), string.Format(Gerecler.ResourcesReason.OutOfBounds, ElementNo.ToString()), Name);
            if (MipNo >= FMipLevels) throw new EInvalidParameter("MipNo", MipNo.GetType(), string.Format(Gerecler.ResourcesReason.OutOfBounds, MipNo.ToString()), Name);

            var SubResourceNo = ElementNo * FMipLevels + MipNo;

            if (MappedSubResources.ContainsKey(SubResourceNo)) throw new EInvalidCall(Name, GetType(), ResourcesReason.SubresourceIsAlreadyMapped, Name);

            var Result = new MappedTexture3D();

            if (Resource is Texture1D)
            {
                var Result2 = ((Texture1D)Resource).Map(SubResourceNo, Type, 0, out Result.Data);
                if (Result2 != 0) throw new EDirectX("Texture1D.Map", Result2, Name);

                Result.Data.InitSize((uint)(Math.Max(1, FSize.Width >> (int)MipNo) * 4));
            }
            else if (Resource is Texture2D)
            {
                MappedTexture2D Out;
                var Result2 = ((Texture2D)Resource).Map(SubResourceNo, Type, 0, out Out);
                if (Result2 != 0) throw new EDirectX("Texture2D.Map", Result2, Name);

                Result.Data = Out.Data;
                Result.RowPitch = Out.RowPitch;

                Result.Data.InitSize((uint)(Math.Max(1, FSize.Height >> (int)MipNo) * Result.RowPitch));
            }
            else if (Resource is Texture3D)
            {
                var Result2 = ((Texture3D)Resource).Map(SubResourceNo, Type, 0, out Result);
                if (Result2 != 0) throw new EDirectX("Texture3D.Map", Result2, Name);

                Result.Data.InitSize((uint)(Math.Max(1, FSize.Height >> (int)MipNo) * Result.DepthPitch));
            }

            MappedSubResources.Add(SubResourceNo, SubResourceNo);

            return Result;
        }

        public void Unmap(uint ElementNo, uint MipNo)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (ElementNo >= FArrayLength) throw new EInvalidParameter("ElementNo", ElementNo.GetType(), string.Format(Gerecler.ResourcesReason.OutOfBounds, ElementNo.ToString()), Name);
            if (MipNo >= FMipLevels) throw new EInvalidParameter("MipNo", MipNo.GetType(), string.Format(Gerecler.ResourcesReason.OutOfBounds, MipNo.ToString()), Name);

            var SubResourceNo = ElementNo * FMipLevels + MipNo;

            if (!MappedSubResources.ContainsKey(SubResourceNo)) throw new EInvalidCall(Name, GetType(), ResourcesReason.SubresourceIsNotMapped, Name);

            if (Resource is Texture1D) ((Texture1D)Resource).Unmap(SubResourceNo);
            else if (Resource is Texture2D) ((Texture2D)Resource).Unmap(SubResourceNo);
            else if (Resource is Texture3D) ((Texture3D)Resource).Unmap(SubResourceNo);

            MappedSubResources.Remove(SubResourceNo);
        }

        public void UnmapAll()
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            foreach (var SubResourceNo in MappedSubResources.Keys)
            {
                if (Resource is Texture1D) ((Texture1D)Resource).Unmap(SubResourceNo);
                else if (Resource is Texture2D) ((Texture2D)Resource).Unmap(SubResourceNo);
                else if (Resource is Texture3D) ((Texture3D)Resource).Unmap(SubResourceNo);
            }

            MappedSubResources.Clear();
        }

        /// <summary>
        /// Starts the texture by encapsulating the resource instead of creating a new one unlike other start methods.
        /// </summary>
        internal void StartFromSwapChainTextureOrDepthStencilTexture(Resource Resource, bool CanStop)
        {
            BindFlag BindFlags = 0;
            Format Format = 0;

            this.CanStop = CanStop;

            // Detect type

            if (Resource is Texture1D)
            {
                Texture1D_Description Description;
                ((Texture1D)Resource).GetDescription(out Description);

                FType = NType.Line;

                BindFlags = Description.BindFlags;
                Format = Description.Format;

                FArrayLength = Description.ArraySize;
                FMipLevels = Description.MipLevels;
                FSize = new SSize3D((int)Description.Width, 1, 1);
            }
            else if (Resource is Texture2D)
            {
                Texture2D_Description Description;
                ((Texture2D)Resource).GetDescription(out Description);

                FType = (Description.MiscellaneousFlags & ResourceMiscellaneousFlag.TextureCube) > 0 ? NType.Cube : NType.Normal;

                BindFlags = Description.BindFlags;
                Format = Description.Format;

                FArrayLength = Description.ArraySize;
                FMipLevels = Description.MipLevels;
                FSize = new SSize3D((int)Description.Width, (int)Description.Height, 1);
            }
            else if (Resource is Texture3D)
            {
                Texture3D_Description Description;
                ((Texture3D)Resource).GetDescription(out Description);

                FType = NType.Volume;

                BindFlags = Description.BindFlags;
                Format = Description.Format;

                FArrayLength = 1;
                FMipLevels = Description.MipLevels;
                FSize = new SSize3D((int)Description.Width, (int)Description.Height, (int)Description.Depth);
            }

            // Detect usage

            FIsDepthStencil = (BindFlags & BindFlag.DepthStencil) > 0;

            // Detect compression

            switch (Format)
            {
            case Format.BC2_UNorm:
                FCompression = NCompression.BC2;
                break;
            case Format.BC3_UNorm:
                FCompression = NCompression.BC3;
                break;
            case Format.D24_UNorm_S8_UInt:
            case Format.R8G8B8A8_UNorm:
                FCompression = NCompression.None;
                break;
            default:
                throw new EGerecler("Texture format", typeof(Format), Gerecler.ResourcesReason.UnexpectedInternalError, Name);
            }

            this.Resource = Resource;

            CreateViews();
        }

        public void Start()
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);
            if (Context.Device == null) throw new EInvalidCall(Context.Name, Context.GetType(), ResourcesReason.NotActive, Name);

            CreateTexture(null);
        }

        public void Start(TImageFileInfo Info)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (FIsDepthStencil) throw new EInvalidCall(Name, GetType(), ResourcesReason.DepthStencilTextureCantBeLoadedOrStartedWithAnyData, Name);

            if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);
            if (Context.Device == null) throw new EInvalidCall(Context.Name, Context.GetType(), ResourcesReason.NotActive, Name);

            if (Info == null) throw new EInvalidParameter("Info", typeof(TImageFileInfo), string.Format(Gerecler.ResourcesReason.Empty), Name);
            if (FType == NType.Line)
            {
                ImageInfo SourceInfo;
                var Result = Functions.GetImageInfoFromFile(Info.FileName, out SourceInfo);
                if (Result != 0) throw new EDirectX("GetImageInfoFromFile", Result, Name);
                if (SourceInfo.ImageFileFormat != ImageFileFormat.DDS) throw new EInvalidCall(Name, GetType(), ResourcesReason.LineTextureCantBeStartedFromImageFileExceptDds, Name);
            }

            CreateTexture(Info);
        }

        public void Start(TImageMemoryFileInfo Info)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (FIsDepthStencil) throw new EInvalidCall(Name, GetType(), ResourcesReason.DepthStencilTextureCantBeLoadedOrStartedWithAnyData, Name);
            if (FType == NType.Line) throw new EInvalidCall(Name, GetType(), ResourcesReason.LineTextureCantBeStartedFromImageFileExceptDds, Name);

            if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);
            if (Context.Device == null) throw new EInvalidCall(Context.Name, Context.GetType(), ResourcesReason.NotActive, Name);

            if (Info == null) throw new EInvalidParameter("Info", typeof(TImageMemoryFileInfo), string.Format(Gerecler.ResourcesReason.Empty), Name);
            if (FType == NType.Line)
            {
                ImageInfo SourceInfo;
                var Result = Functions.GetImageInfoFromMemory(Info.MemoryFile, out SourceInfo);
                if (Result != 0) throw new EDirectX("GetImageInfoFromMemory", Result, Name);
                if (SourceInfo.ImageFileFormat != ImageFileFormat.DDS) throw new EInvalidCall(Name, GetType(), ResourcesReason.LineTextureCantBeStartedFromImageFileExceptDds, Name);
            }

            CreateTexture(Info);
        }

        public void Start(params SubResourceData[] Data)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (FIsDepthStencil) throw new EInvalidCall(Name, GetType(), ResourcesReason.DepthStencilTextureCantBeLoadedOrStartedWithAnyData, Name);

            if (Resource != null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotInactive, Name);
            if (Context.Device == null) throw new EInvalidCall(Context.Name, Context.GetType(), ResourcesReason.NotActive, Name);

            if (Data == null || Data.Length == 0) throw new EInvalidParameter("Data", typeof(SubResourceData[]), string.Format(Gerecler.ResourcesReason.Empty), Name);

            CreateTexture(Data);
        }

        public void SaveToFile(string FileName, ImageFileFormat FileFormat)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (FType == NType.Line && FileFormat != ImageFileFormat.DDS) throw new EInvalidCall(Name, GetType(), ResourcesReason.LineTextureCantBeSavedToImageFileExceptDds, Name);
            if (FIsDepthStencil && FileFormat != ImageFileFormat.DDS) throw new EInvalidCall(Name, GetType(), ResourcesReason.DepthStencilTextureCantBeSavedToImageFileExceptDds, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (string.IsNullOrEmpty(FileName)) throw new EInvalidParameter("FileName", typeof(string), string.Format(Gerecler.ResourcesReason.Empty), Name);

            if (MappedSubResources.Count > 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.CantExecuteWhileAnySubResourceIsMapped, Name);

            var Extension = Path.GetExtension(FileName);
            FileName = Path.GetFileNameWithoutExtension(FileName);

            var Length = FArrayLength;
            if (FType == NType.Volume) Length = (uint)FSize.Depth;

            var NumberedFileName = Length != 1 || FMipLevels != 1;

            if (FileFormat == ImageFileFormat.DDS || (FCompression == NCompression.None && Length == 1 && FMipLevels == 1 && FType != NType.Volume))
            {
                var Result = Functions.SaveTextureToFile(Resource, FileFormat, FileName + Extension);
                if (Result != 0) throw new EDirectX("SaveTextureToFile", Result, Name);
            }
            else
            {
                var Width = (uint)FSize.Width;
                uint Height;
                switch (FType)
                {
                case NType.Line:
                    Height = 1;
                    break;
                case NType.Cube:
                    Height = (uint)FSize.Width;
                    break;
                default:
                    Height = (uint)FSize.Height;
                    break;
                }

                for (var MipNo = 0u; MipNo < FMipLevels; MipNo++)
                {
                    for (var ElementNo = 0u; ElementNo < Length; ElementNo++)
                    {
                        var TempTextureDescription = new Texture2D_Description
                        {
                            Usage = Usage.Staging,
                            CpuAccessFlags = CpuAccessFlag.Read,
                            SampleDescription = new SampleDescription { Count = 1, Quality = 0 },
                            ArraySize = 1,
                            Width = Width,
                            Height = Height,
                            MipLevels = 1,
                            MiscellaneousFlags = 0,
                            BindFlags = 0,
                            Format = Format.R8G8B8A8_UNorm
                        };

                        Texture2D Texture2D;
                        var Result = Context.Device.CreateTexture2D(ref TempTextureDescription, null, out Texture2D);
                        try
                        {
                            if (Result != 0) throw new EDirectX("Device.CreateTexture2D", Result, Name);

                            var LoadInfo = new TextureLoadInfo
                            {
                                DestinationBox = new Box { Right = Width, Bottom = Height },
                                NumberOfElements = 1,
                                NumberOfMips = 1,
                                SourceFirstMip = MipNo,
                                Filter = FilterFlag.None,
                                MipFilter = FilterFlag.None,
                            };

                            if (FType == NType.Volume) LoadInfo.SourceBox = new Box { Right = Width, Bottom = Height, Front = ElementNo, Back = ElementNo + 1 };
                            else
                            {
                                LoadInfo.SourceBox = new Box { Right = Width, Bottom = Height };
                                LoadInfo.SourceFirstElement = ElementNo;
                            }

                            Result = Functions.LoadTextureFromTexture(Resource, ref LoadInfo, Texture2D);
                            if (Result != 0) throw new EDirectX("LoadTextureFromTexture", Result, Name);

                            var FullFileName = FileName;
                            if (NumberedFileName) FullFileName += "_" + MipNo + "_" + ElementNo;
                            FullFileName += Extension;

                            Result = Functions.SaveTextureToFile(Texture2D, FileFormat, FullFileName);
                            if (Result != 0) throw new EDirectX("SaveTextureToFile", Result, Name);
                        }
                        finally
                        {
                            if (Texture2D != null) Texture2D.Release();
                        }
                    }

                    Width /= 2;
                    Height /= 2;
                    if (FType == NType.Volume) Length /= 2;
                }
            }
        }

        public List<Blob> SaveToMemoryFile(ImageFileFormat FileFormat)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (FType == NType.Line && FileFormat != ImageFileFormat.DDS) throw new EInvalidCall(Name, GetType(), ResourcesReason.LineTextureCantBeSavedToImageFileExceptDds, Name);
            if (FIsDepthStencil && FileFormat != ImageFileFormat.DDS) throw new EInvalidCall(Name, GetType(), ResourcesReason.DepthStencilTextureCantBeSavedToImageFileExceptDds, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (MappedSubResources.Count > 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.CantExecuteWhileAnySubResourceIsMapped, Name);

            var Length = FArrayLength;
            if (FType == NType.Volume) Length = (uint)FSize.Depth;

            var Result = new List<Blob>();

            if (FileFormat == ImageFileFormat.DDS || (FCompression == NCompression.None && Length == 1 && FMipLevels == 1 && FType != NType.Volume))
            {
                Blob Blob;
                var Result2 = Functions.SaveTextureToMemory(Resource, FileFormat, out Blob, 0);
                if (Result2 != 0) throw new EDirectX("SaveTextureToMemory", Result2, Name);
                Result.Add(Blob);
            }
            else
            {
                var Width = (uint)FSize.Width;
                uint Height;
                switch (FType)
                {
                case NType.Line:
                    Height = 1;
                    break;
                case NType.Cube:
                    Height = (uint)FSize.Width;
                    break;
                default:
                    Height = (uint)FSize.Height;
                    break;
                }

                try
                {
                    for (var MipNo = 0u; MipNo < FMipLevels; MipNo++)
                    {
                        for (var ArrayNo = 0u; ArrayNo < Length; ArrayNo++)
                        {
                            var TempTextureDescription = new Texture2D_Description
                            {
                                Usage = Usage.Staging,
                                CpuAccessFlags = CpuAccessFlag.Read,
                                SampleDescription = new SampleDescription { Count = 1, Quality = 0 },
                                ArraySize = 1,
                                Width = Width,
                                Height = Height,
                                MipLevels = 1,
                                MiscellaneousFlags = 0,
                                BindFlags = 0,
                                Format = Format.R8G8B8A8_UNorm
                            };

                            Texture2D Texture2D;
                            var Result2 = Context.Device.CreateTexture2D(ref TempTextureDescription, null, out Texture2D);
                            try
                            {
                                if (Result2 != 0) throw new EDirectX("Device.CreateTexture2D", Result2, Name);

                                var LoadInfo = new TextureLoadInfo
                                {
                                    DestinationBox = new Box { Right = Width, Bottom = Height },
                                    NumberOfElements = 1,
                                    NumberOfMips = 1,
                                    SourceFirstMip = MipNo,
                                    Filter = FilterFlag.None,
                                    MipFilter = FilterFlag.None,
                                };

                                if (FType == NType.Volume) LoadInfo.SourceBox = new Box { Right = Width, Bottom = Height, Front = ArrayNo, Back = ArrayNo + 1 };
                                else
                                {
                                    LoadInfo.SourceBox = new Box { Right = Width, Bottom = Height };
                                    LoadInfo.SourceFirstElement = ArrayNo;
                                }

                                Result2 = Functions.LoadTextureFromTexture(Resource, ref LoadInfo, Texture2D);
                                if (Result2 != 0) throw new EDirectX("LoadTextureFromTexture", Result2, Name);

                                Blob Blob;
                                Result2 = Functions.SaveTextureToMemory(Texture2D, FileFormat, out Blob, 0);
                                if (Result2 != 0) throw new EDirectX("SaveTextureToMemory", Result2, Name);
                                Result.Add(Blob);
                            }
                            finally
                            {
                                if (Texture2D != null) Texture2D.Release();
                            }
                        }

                        Width /= 2;
                        Height /= 2;
                        if (FType == NType.Volume) Length /= 2;
                    }
                }
                catch
                {
                    foreach (var Blob in Result.Where(Blob => Blob != null))
                    {
                        Blob.Release();
                    }

                    throw;
                }
            }

            return Result;
        }

        public void Clear(ref Float4 Color)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (RenderTargetView == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotRenderTarget, Name);
            if (MappedSubResources.Count > 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.CantExecuteWhileAnySubResourceIsMapped, Name);

            Context.Device.ClearRenderTargetView(RenderTargetView, ref Color);
        }

        public void Clear(ClearFlag Flags, float DepthValue, byte StencilValue)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (DepthStencilView == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotDepthStencil, Name);
            if (MappedSubResources.Count > 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.CantExecuteWhileAnySubResourceIsMapped, Name);

            Context.Device.ClearDepthStencilView(DepthStencilView, Flags, DepthValue, StencilValue);
        }

        public TTexture DepthStencilPair;

        public void SetOutputTarget()
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (FCompression != NCompression.None) throw new EInvalidCall(Name, GetType(), ResourcesReason.CompressedTextureCantBeOutputTarget, Name);
            if (RenderTargetView == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotRenderTarget, Name);
            if (MappedSubResources.Count > 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.CantExecuteWhileAnySubResourceIsMapped, Name);

            var PairDepthStencilView = (DepthStencilPair != null) && (DepthStencilPair.Resource != null) ? DepthStencilPair.DepthStencilView : null;

            Context.Device.OM_SetRenderTargets(1, new[] { RenderTargetView }, PairDepthStencilView);
        }

        public void LoadFromTexture(TTexture Texture, ref TextureLoadInfo LoadInfo)
        {
            if (Context == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.DeletedAndCantExecuteAnything, Name);

            if (FIsDepthStencil) throw new EInvalidCall(Name, GetType(), ResourcesReason.DepthStencilTextureCantBeLoadedOrStartedWithAnyData, Name);

            if (Resource == null) throw new EInvalidCall(Name, GetType(), ResourcesReason.NotActive, Name);

            if (Texture == null) throw new EInvalidParameter("Texture", typeof(TTexture), string.Format(Gerecler.ResourcesReason.Empty), Name);
            if (Texture.Resource == null) throw new EInvalidCall(Texture.Name, Texture.GetType(), ResourcesReason.NotActive, Name);

            if (Context != Texture.Context) throw new EInvalidCall(Texture.Name, Texture.GetType(), ResourcesReason.ParentContextsNotMatching, Name);
            if (MappedSubResources.Count > 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.CantExecuteWhileAnySubResourceIsMapped, Name);

            if (Resource is Texture2D)
            {
                Texture2D_Description Description;
                ((Texture2D)Resource).GetDescription(out Description);
                var SampleDescription = Description.SampleDescription;

                if (SampleDescription.Count != 1 || SampleDescription.Quality != 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.MultisampledTextureCantBeOperandOfLoadFromTexture, Name);
            }
            if (Texture.Resource is Texture2D)
            {
                Texture2D_Description Description;
                ((Texture2D)Texture.Resource).GetDescription(out Description);
                var SampleDescription = Description.SampleDescription;

                if (SampleDescription.Count != 1 || SampleDescription.Quality != 0) throw new EInvalidCall(Name, GetType(), ResourcesReason.MultisampledTextureCantBeOperandOfLoadFromTexture, Name);
            }

            var Result = Functions.LoadTextureFromTexture(Texture.Resource, ref LoadInfo, Resource);
            if (Result != 0) throw new EDirectX("LoadTextureFromTexture", Result, Name);
        }
    }
}